package com.zego.zegowawaji_server.device;

import android.content.Context;

import com.zego.base.utils.AppLogger;
import com.zego.zegowawaji_server.utils.JsonKey;

import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.util.Random;

/**
 * 雪暴娃娃机
 */
public class XueBaoWawaji extends WawajiDevice {
    static final private int BAUD_RATE = 115200;

    static final private byte[] CMD_BYTE_BEGIN = {(byte) 0xfe, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0xff, (byte) 0xff, (byte) 0x10, (byte) 0x31, (byte) 0x3c, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x1d};
    static final private byte[] CMD_BYTE_BEGIN_GET = {(byte) 0xfe, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0xff, (byte) 0xff, (byte) 0x10, (byte) 0x31, (byte) 0x3c, (byte) 0x00, (byte) 0x28, (byte) 0x20, (byte) 0x28, (byte) 0x28, (byte) 0x07, (byte) 0x1e};
    static final private byte[] CMD_BYTE_FORWARD = {(byte) 0xfe, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0xff, (byte) 0xff, (byte) 0x0c, (byte) 0x32, (byte) 0x00, (byte) 0x2c, (byte) 0x01, (byte) 0x07};
    static final private byte[] CMD_BYTE_BACKWARD = {(byte) 0xfe, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0xff, (byte) 0xff, (byte) 0x0c, (byte) 0x32, (byte) 0x01, (byte) 0x2c, (byte) 0x01, (byte) 0x08};
    static final private byte[] CMD_BYTE_LEFT = {(byte) 0xfe, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0xff, (byte) 0xff, (byte) 0x0c, (byte) 0x32, (byte) 0x02, (byte) 0x2c, (byte) 0x01, (byte) 0x09};
    static final private byte[] CMD_BYTE_RIGHT = {(byte) 0xfe, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0xff, (byte) 0xff, (byte) 0x0c, (byte) 0x32, (byte) 0x03, (byte) 0x2c, (byte) 0x01, (byte) 0x0a};
    static final private byte[] CMD_BYTE_DOWN = {(byte) 0xfe, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0xff, (byte) 0xff, (byte) 0x0b, (byte) 0x32, (byte) 0x04, (byte) 0x00, (byte) 0x41};

    private Random mRandom = new Random();
    private DeviceStateListener mListener;

    public XueBaoWawaji(DeviceStateListener listener) throws SecurityException, IOException {
        super(new File("/dev/ttyS1"), BAUD_RATE, Context.MODE_PRIVATE);
        mListener = listener;
        mRandom = new Random();

        Thread readThread = new ReadThread("xuebao-reader");
        readThread.start();
    }

    @Override
    public boolean sendBeginCommand(JSONObject gameJson, int seq) {
        byte[] cmdData = CMD_BYTE_BEGIN;

        if (gameJson != null) {
            int catchPR = gameJson.optInt(JsonKey.KEY_CATCH_PR);

            if (catchPR == 100) {
                int index = 10;
                //cmdData[index++] = (mRandom.nextInt(catchPR) == 1) ? (byte) 1 : (byte) 0;     // 是否中奖：system_config: 0 表示使用指令中指定的爪力值； 1表示使用系统设置的最大爪力值，此时需要将num6 设置为最大值
                cmdData[index++] = (byte) (mRandom.nextInt(8) + 30);    // 抓起爪力(1—48)
                cmdData[index++] = (byte) (mRandom.nextInt(8) + 30);    // 到顶爪力(1—48)
                cmdData[index++] = (byte) (mRandom.nextInt(8) + 30);    // 移动爪力(1—48)
                cmdData[index++] = (byte) (mRandom.nextInt(8) + 30);    // 大爪力(1—48)
                cmdData[index++] = (byte) (mRandom.nextInt(5) + 5);        // 抓起高度（0--10）

                int sum = 0;
                for (int i = 6; i < cmdData.length - 1; i++) {
                    sum += (cmdData[i] & 0xff);
                }
                cmdData[cmdData.length - 1] = (byte) (sum % 100); // 检验位
            } else {
//                int catchUpRand = gameJson.optInt(JsonKey.KEY_CATCH_UP_RAND);
//                int catchUpConst = gameJson.optInt(JsonKey.KEY_CATCH_UP_CONST);
//                int catchTopRand = gameJson.optInt(JsonKey.KEY_CATCH_TOP_RAND);
//                int catchTopConst = gameJson.optInt(JsonKey.KEY_CATCH_TOP_CONST);
//                int catchMoveRand = gameJson.optInt(JsonKey.KEY_CATCH_MOVE_RAND);
//                int catchMoveConst = gameJson.optInt(JsonKey.KEY_CATCH_MOVE_CONST);
//                int catchBigRand = gameJson.optInt(JsonKey.KEY_CATCH_BIG_RAND);
//                int catchBigConst = gameJson.optInt(JsonKey.KEY_CATCH_BIG_CONST);
//                int catchHeightRand = gameJson.optInt(JsonKey.KEY_CATCH_HEIGHT_RAND);
//                int catchHeightConst = gameJson.optInt(JsonKey.KEY_CATCH_HEIGHT_CONST);

                int catchUpRand = 8;
                int catchUpConst = 18;
                int catchTopRand = 5;
                int catchTopConst = 2;
                int catchMoveRand = 10;
                int catchMoveConst = 5;
                int catchBigRand = 10;
                int catchBigConst = 1;
                int catchHeightRand = 4;
                int catchHeightConst = 6;

                try {
                    Object upRand = gameJson.opt(JsonKey.KEY_CATCH_UP_RAND);
                    if (upRand instanceof String) {
                        catchUpRand = Integer.valueOf((String) upRand);
                    } else {
                        catchUpRand = (int) upRand;
                    }

                    Object upConst = gameJson.opt(JsonKey.KEY_CATCH_UP_CONST);
                    if (upConst instanceof String) {
                        catchUpConst = Integer.valueOf((String) upConst);
                    } else {
                        catchUpConst = (int) upConst;
                    }

                    Object topRand = gameJson.opt(JsonKey.KEY_CATCH_TOP_RAND);
                    if (topRand instanceof String) {
                        catchTopRand = Integer.valueOf((String) topRand);
                    } else {
                        catchTopRand = (int) topRand;
                    }

                    Object topConst = gameJson.opt(JsonKey.KEY_CATCH_TOP_CONST);
                    if (topConst instanceof String) {
                        catchTopConst = Integer.valueOf((String) topConst);
                    } else {
                        catchTopConst = (int) topConst;
                    }

                    Object moveRand = gameJson.opt(JsonKey.KEY_CATCH_MOVE_RAND);
                    if (moveRand instanceof String) {
                        catchMoveRand = Integer.valueOf((String) moveRand);
                    } else {
                        catchMoveRand = (int) moveRand;
                    }

                    Object moveConst = gameJson.opt(JsonKey.KEY_CATCH_MOVE_CONST);
                    if (moveConst instanceof String) {
                        catchMoveConst = Integer.valueOf((String) moveConst);
                    } else {
                        catchMoveConst = (int) moveConst;
                    }

                    Object bigRand = gameJson.opt(JsonKey.KEY_CATCH_BIG_RAND);
                    if (bigRand instanceof String) {
                        catchBigRand = Integer.valueOf((String) bigRand);
                    } else {
                        catchBigRand = (int) bigRand;
                    }

                    Object bigConst = gameJson.opt(JsonKey.KEY_CATCH_BIG_CONST);
                    if (bigConst instanceof String) {
                        catchBigConst = Integer.valueOf((String) bigConst);
                    } else {
                        catchBigConst = (int) bigConst;
                    }

                    Object heightRand = gameJson.opt(JsonKey.KEY_CATCH_HEIGHT_RAND);
                    if (heightRand instanceof String) {
                        catchHeightRand = Integer.valueOf((String) heightRand);
                    } else {
                        catchHeightRand = (int) heightRand;
                    }

                    Object heightConst = gameJson.opt(JsonKey.KEY_CATCH_HEIGHT_CONST);
                    if (heightConst instanceof String) {
                        catchHeightConst = Integer.valueOf((String) heightConst);
                    } else {
                        catchHeightConst = (int) heightConst;
                    }
                } catch (Exception e) {
                }

                int index = 9;
                cmdData[index++] = (mRandom.nextInt(catchPR) == 1) ? (byte) 1 : (byte) 0;     // 是否中奖：system_config: 0 表示使用指令中指定的爪力值； 1表示使用系统设置的最大爪力值，此时需要将num6 设置为最大值
                cmdData[index++] = (byte) (mRandom.nextInt(catchUpRand) + catchUpConst);    // 抓起爪力(1—48)
                cmdData[index++] = (byte) (mRandom.nextInt(catchTopRand) + catchTopConst);    // 到顶爪力(1—48)
                cmdData[index++] = (byte) (mRandom.nextInt(catchMoveRand) + catchMoveConst);    // 移动爪力(1—48)
                cmdData[index++] = (byte) (mRandom.nextInt(catchBigRand) + catchBigConst);    // 大爪力(1—48)
                cmdData[index++] = (byte) (mRandom.nextInt(catchHeightRand) + catchHeightConst);        // 抓起高度（0--10）

                int sum = 0;
                for (int i = 6; i < cmdData.length - 1; i++) {
                    sum += (cmdData[i] & 0xff);
                }
                cmdData[cmdData.length - 1] = (byte) (sum % 100); // 检验位
            }
        } else {   // 数据拿失败，用默认随机
            int index = 9;
            cmdData[index++] = (mRandom.nextInt(30) == 1) ? (byte) 1 : (byte) 0;     // 是否中奖：system_config: 0 表示使用指令中指定的爪力值； 1表示使用系统设置的最大爪力值，此时需要将num6 设置为最大值
            cmdData[index++] = (byte) (mRandom.nextInt(8) + 18);    // 抓起爪力(1—48)
            cmdData[index++] = (byte) (mRandom.nextInt(5) + 2);    // 到顶爪力(1—48)
            cmdData[index++] = (byte) (mRandom.nextInt(10) + 5);    // 移动爪力(1—48)
            cmdData[index++] = (byte) (mRandom.nextInt(10) + 1);    // 大爪力(1—48)
            cmdData[index++] = (byte) (mRandom.nextInt(4) + 6);        // 抓起高度（0--10）

            int sum = 0;
            for (int i = 6; i < cmdData.length - 1; i++) {
                sum += (cmdData[i] & 0xff);
            }
            cmdData[cmdData.length - 1] = (byte) (sum % 100); // 检验位
        }
        updateSequence(cmdData, seq);

        return sendCommandData(cmdData);
    }

    @Override
    public boolean sendForwardCommand(int seq) {
        return sendCommandData(updateSequence(CMD_BYTE_FORWARD, seq));
    }

    @Override
    public boolean sendBackwardCommand(int seq) {
        return sendCommandData(updateSequence(CMD_BYTE_BACKWARD, seq));
    }

    @Override
    public boolean sendLeftCommand(int seq) {
        return sendCommandData(updateSequence(CMD_BYTE_LEFT, seq));
    }

    @Override
    public boolean sendRightCommand(int seq) {
        return sendCommandData(updateSequence(CMD_BYTE_RIGHT, seq));
    }

    @Override
    public boolean sendGrabCommand(int seq) {
        return sendCommandData(updateSequence(CMD_BYTE_DOWN, seq));
    }

    @Override
    public boolean checkDeviceState() {
        return false;
    }

    private byte[] updateSequence(byte[] data, int seq) {
        data[1] = (byte) ((seq >> 8) & 0xff);
        data[2] = (byte) (seq & 0xff);
        data[4] = (byte) (~data[1] & 0xff);
        data[5] = (byte) (~data[2] & 0xff);
        return data;
    }

    /**
     * 收到下位机的指令包
     *
     * @param data
     * @param size
     */
    private void onResponseCommandReceived(byte[] data, int size) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(Integer.toHexString((data[i] & 0x000000FF) | 0xFFFFFF00).substring(6));
        }

        AppLogger.getInstance().writeLog("receive: %s from XueBao device. data size: %d", sb.toString(), size);

        if (data[7] == (byte) 0x33) {    // 游戏结束返回
            boolean win = (data[8] == (byte) 0x01);
            if (mListener != null) {
                mListener.onGameOver(win);
            }
        }
    }

    private class ReadThread extends Thread {
        private byte[] cmdBuffer = new byte[512];   // 读取的待分析数据
        private int bufferLength = 0;
        private int currentCmdLength = 0;

        public ReadThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            int size;
            byte[] buffer = new byte[64];

            while (!isInterrupted()) {
                if (mFileInputStream == null) break;

                try {
                    size = mFileInputStream.read(buffer);
                    for (int i = 0; i < size; i++) {
                        byte b = buffer[i];
                        if (bufferLength == 0 && b != (byte) 0xfe) {
                            continue;
                        }
                        cmdBuffer[bufferLength++] = buffer[i];
                    }

                    while (bufferLength >= 8) { // 包头6字节+长度位1字节+命令位1字节
                        if ((cmdBuffer[0] & cmdBuffer[3]) == 0
                                && (cmdBuffer[1] & cmdBuffer[4]) == 0
                                && (cmdBuffer[2] & cmdBuffer[5]) == 0) {    // 合法
                            currentCmdLength = cmdBuffer[6] & 0xff;

                            if (bufferLength >= currentCmdLength) {
                                boolean isValidate = checkCmdData(cmdBuffer, currentCmdLength);
                                if (isValidate) {
                                    onResponseCommandReceived(cmdBuffer, currentCmdLength);
                                }

                                int j = 0;
                                for (int i = currentCmdLength; i < bufferLength; i++) {
                                    cmdBuffer[j++] = cmdBuffer[i];
                                }
                                cmdBuffer[j] = '\0';
                                bufferLength = j;
                            } else {
                                break;
                            }
                        } else {    // 不合法，查找下一个 0xfe
                            int pos = -1;

                            StringBuilder tmpBuilder = new StringBuilder();
                            for (int i = 0; i < bufferLength; i++) {
                                byte b = cmdBuffer[i];
                                if (b == (byte) 0xfe) {
                                    pos = i;
                                    break;
                                }
                                tmpBuilder.append(Integer.toHexString((b & 0x000000FF) | 0xFFFFFF00).substring(6));
                            }

                            AppLogger.getInstance().writeLog("**** invalid data: %s *****", tmpBuilder.toString());
                            if (pos > 0) {
                                int j = 0;
                                for (int i = pos; i < bufferLength; i++) {
                                    cmdBuffer[j++] = cmdBuffer[i];
                                }
                                cmdBuffer[j] = '\0';
                                bufferLength = j;
                            } else {
                                cmdBuffer[0] = '\0';
                                bufferLength = 0;
                            }
                        }
                    }
                } catch (IOException e) {
                    AppLogger.getInstance().writeLog("DeviceManager's ReadThread Exception. e : %s", e);
                    break;
                }


                if (size < buffer.length) { // 如果不能填满缓冲区，则等待 500ms 后再读，否则立即读取下一段 buffer
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        AppLogger.getInstance().writeLog("DeviceManager's ReadThread wait Exception. e : ", e);
                    }
                }
            }
        }

        private boolean checkCmdData(byte[] data, int length) {
            int sum = 0;
            for (int i = 6; i < length - 1; i++) {  // 最后的校验位不参与校验
                sum += data[i];
            }
            return sum % 100 == data[length - 1];
        }
    }
}
